/*

    This file is part of GLua

    GLua Development Environment
    Copyright (C) 2007 VoiDeD

    GLua is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    GLua is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
using System.Diagnostics;
using QWhale.Editor;
using QWhale.Syntax;
using TD.SandDock;

namespace GLua
{
    /// <summary>
    /// This is the base environment class which manages all aspects of GLua's interface
    /// </summary>
    public class EnvironmentManager
    {
        /* 
         * I was not to proud of this class when I started work on GLua
         * It's poorly coded, and gets the job done slowly
         * After I realized that all this could be reorganized into an easier solution, it was too late
         * to completely recode this class, and because of general hardship created by this class
         * class and other various personal issues, I am open sourcing GLua in hopes that someone will be
         * willing to continue work on GLua
         * 
         * If any brave soul is determined enough to finish the work I started then good luck to you!
         * 
        */

        // the sanddock manager which manages dockable windows, tabbed documents, etc
        SandDockManager manager;

        // the treeview used in the Project Explorer
        TreeView treeView;
        // just an imagelist which defines various images used by the Project Explorer
        ImageList imageList;

        // code provider
        CodeProvider code;
        // currently open project, should be set to null when no project is opened
        Project project;
        // current list of settings
        Settings settings;

        // context menu used when right clicking tabs
        ContextMenuStrip tabStrip;
        // used when right clicking the edit control
        ContextMenuStrip editStrip;
        // used when right clicking on a project node on the treeview
        ContextMenuStrip projectStrip;
        // used when right clicking on a code node on the treeview
        ContextMenuStrip codeStrip;
        // used when right clicking a folder node on the treeview
        ContextMenuStrip folderStrip;

        // project explorer window
        DockableWindow explorer;
        // output window (unused at the moment)
        DockableWindow output;
        // task list window
        DockableWindow taskList;

        // set to true when a file is being loaded so that multiple clicks on the treeview don't crash GLua
        bool fileLoading = false;

        /// <summary>
        /// Creates a new Environment class
        /// </summary>
        /// <param name="sdm">The sanddock manager used by GLua</param>
        /// <param name="view">A treeview for displaying the project explorer</param>
        /// <param name="images">Image list for the treeview</param>
        /// <param name="tStrip">Tab context menu</param>
        /// <param name="eStrip">Edit control context menu</param>
        /// <param name="pStrip">Project node context menu</param>
        /// <param name="cStrip">Code node context menu</param>
        /// <param name="fStrip">Folder node context menu</param>
        /// <param name="exp">Document explorer window</param>
        /// <param name="tl">Task list window</param>
        /// <param name="outp">Output window</param>
        public EnvironmentManager( SandDockManager sdm, TreeView view, ImageList images, ContextMenuStrip tStrip, ContextMenuStrip eStrip, ContextMenuStrip pStrip, ContextMenuStrip cStrip, ContextMenuStrip fStrip, DockableWindow exp, DockableWindow tl, DockableWindow outp )
        {
            // set all the values
            manager = sdm;
            treeView = view;
            imageList = images;
            tabStrip = tStrip;
            editStrip = eStrip;
            projectStrip = pStrip;
            codeStrip = cStrip;
            folderStrip = fStrip;
            explorer = exp;
            output = outp;
            taskList = tl;

            // used to set the right clicked node as the current node
            treeView.NodeMouseClick += new TreeNodeMouseClickEventHandler( treeView_NodeMouseClick );

            // load the code database and settings
            code = CodeProvider.Load( Application.StartupPath + "\\data\\gmod_lua.xml" );
            settings = Settings.Load( Application.StartupPath + "\\data\\settings.xml" );
        }

        /// <summary>
        /// Checks if a project is open
        /// </summary>
        /// <returns>True if a project is open, otherwise false</returns>
        public bool IsProjectOpen()
        {
            return ( project != null );
        }
        /// <summary>
        /// Checks if the current project is saved, does not check if a project is open
        /// </summary>
        /// <returns>True if saved, otherwise false</returns>
        public bool IsProjectSaved()
        {
            return project.IsProjectSaved();
        }
        /// <summary>
        /// Checks if a specific project file is shown in the interface
        /// </summary>
        /// <param name="file">The project file to check</param>
        /// <returns>True if the file is open, otherwise false</returns>
        public bool IsProjectFileShown( ProjectFile file )
        {
            foreach ( TabbedDocument tp in manager.Documents )
            {
                if ( !IsSpecialTab( tp ) )
                {
                    if ( tp.Tag == file )
                        return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Checks if a special tab is open, such as the Object Browser
        /// </summary>
        /// <param name="tab">The special tab to check</param>
        /// <returns>True if the tab is shown, otherwise false</returns>
        public bool IsSpecialTabShown( SpecialTab tab )
        {
            foreach ( TabbedDocument tp in manager.Documents )
            {
                if ( IsSpecialTab( tp ) )
                {
                    if ( ( ( SpecialTab )tp.Tag ).Type == tab.Type )
                        return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Checks if the object browser window is shown
        /// </summary>
        /// <returns>Returns true if the object browser is shown, otherwise false</returns>
        public bool IsObjectBrowserShown()
        {
            foreach ( TabbedDocument tp in manager.Documents )
            {
                if ( IsSpecialTab( tp ) )
                {
                    if ( ( ( SpecialTab )tp.Tag ).Type == TabType.ObjectBrowser )
                        return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Checks if the project info window is shown
        /// </summary>
        /// <returns>Returns true if the project info window is shown, otherwise false</returns>
        public bool IsProjectInfoShown()
        {
            foreach ( TabbedDocument tp in manager.Documents )
            {
                if ( IsSpecialTab( tp ) )
                {
                    if ( ( ( SpecialTab )tp.Tag ).Type == TabType.ProjectInfo )
                        return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Checks if a tab window is a special tab
        /// </summary>
        /// <param name="tp">The window to check</param>
        /// <returns>True if the window is a special tab, otherwise false</returns>
        public bool IsSpecialTab( TabbedDocument tp )
        {
            return ( tp.Tag is SpecialTab );
        }
        /// <summary>
        /// Checks if a specific dockable window is a special tab
        /// </summary>
        /// <param name="tp">The window to check</param>
        /// <returns>True if the window is a special tab, otherwise false</returns>
        public bool IsSpecialTab( DockControl tp )
        {
            return ( ( tp is TabbedDocument ) && ( tp.Tag is SpecialTab ) );
        }
        /// <summary>
        /// Checks if any tabs are open
        /// </summary>
        /// <returns>True if a tab is open, otherwise false</returns>
        public bool IsTabOpen()
        {
            return ( manager.Documents.Length > 0 );
        }
        /// <summary>
        /// Checks if any code tabs are open
        /// </summary>
        /// <returns>True if a tab is open, otherwise false</returns>
        public bool IsCodeTabOpen()
        {
            if ( IsTabOpen() )
            {
                foreach ( TabbedDocument td in manager.Documents )
                {
                    if ( IsCodeTab( td ) )
                        return true;
                }
                return false;
            }
            return false;
        }
        /// <summary>
        /// Checks if the active tab is a special tab
        /// </summary>
        /// <returns>True if the tab is a special tab, otherwise false</returns>
        public bool IsActiveTabSpecial()
        {
            return IsSpecialTab( (TabbedDocument)manager.ActiveTabbedDocument );
        }
        /// <summary>
        /// Checks if a tab is a code tab
        /// </summary>
        /// <param name="tp">The tab to check</param>
        /// <returns>True if the tab is a code tab, otherwise false</returns>
        public bool IsCodeTab( TabbedDocument tp )
        {
            return (( tp is TabbedDocument) && !(tp.Tag is SpecialTab));
        }
        /// <summary>
        /// Checks if a window is a code tab
        /// </summary>
        /// <param name="tp">The window to check</param>
        /// <returns>True if the window is a code tab, otherwise false</returns>
        public bool IsCodeTab( DockControl tp )
        {
            return ( ( tp is TabbedDocument ) && !( tp.Tag is SpecialTab ) );
        }
        /// <summary>
        /// Checks if a specific char is a char that strings should be split by, used for auto-completion
        /// </summary>
        /// <param name="chr">The character to check</param>
        /// <returns>True if the char is a split char, otherwise false</returns>
        public bool IsSplitChar( char chr )
        {
            return (
                ( chr == ' ' ) ||
                ( chr == '\t' ) ||
                ( chr == '\r' ) ||
                ( chr == '\n' ) ||
                ( chr == '(' ) ||
                ( chr == '|' ) ||
                ( chr == '!' ) ||
                ( chr == '.' ) ||
                ( chr == ':' ) ||
                ( chr == '&' ) ||
                ( chr == '~' ) ||
                ( chr == '-')
            );
        }
        /// <summary>
        /// Checks if a file in the project exists by name
        /// </summary>
        /// <param name="name">The filename to check</param>
        /// <returns>True if the file exists, otherwise false</returns>
        public bool DoesProjectFileExist( string name )
        {
            if ( IsProjectOpen() )
            {
                foreach ( ProjectFile pf in project.Files )
                {
                    if ( pf.Name == name )
                        return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Returns the name of the currently focused tab
        /// </summary>
        /// <returns>The name of the tab</returns>
        public string GetActiveTabName()
        {
            if ( IsTabOpen() )
            {
                return manager.ActiveTabbedDocument.Text;
            }
            else
            {
                return "";
            }
        }
        /// <summary>
        /// Returns a tree node object of the currently focused node
        /// </summary>
        /// <returns>The treenode</returns>
        public TreeNode GetSelectedTreeNode()
        {
            if ( treeView.SelectedNode != null )
                return treeView.SelectedNode;
            else
                return null;
        }

        /// <summary>
        /// Clears all nodes and tabs
        /// </summary>
        public void Clear()
        {
            ClearNodes();
            ClearTabs();
        }
        /// <summary>
        /// Clears all the nodes in the treeview
        /// </summary>
        public void ClearNodes()
        {
            treeView.Nodes.Clear();
        }
        /// <summary>
        /// Clears all tabs in the environment, but keeps the object browser open
        /// </summary>
        public void ClearTabs()
        {
            foreach ( TabbedDocument tp in manager.Documents )
            {
                if ( IsSpecialTab( tp ) )
                {
                    if ( ( ( SpecialTab )tp.Tag ).Type == TabType.ObjectBrowser )
                        continue;
                }
                tp.Close();
            }
        }
        /// <summary>
        /// Refreshes the nodes of the current project
        /// </summary>
        public void ReloadNodes()
        {
            ClearNodes();
            AddProject( project );
        }
        /// <summary>
        /// Removes a specific project file node
        /// </summary>
        /// <param name="file">The project file to remove</param>
        public void RemoveNode( ProjectFile file )
        {
            if ( IsProjectOpen() )
            {
                foreach ( TreeNode tn in treeView.Nodes[0].Nodes )
                {
                    if ( tn.Tag is ProjectFile )
                    {
                        if ( tn.Tag == file )
                            tn.Remove();
                    }
                }
            }
        }
        /// <summary>
        /// Renames an existing project file node to something else
        /// </summary>
        /// <param name="file">The file to rename</param>
        /// <param name="newName">The new name to give the file</param>
        public void RenameNode( ProjectFile file, string newName )
        {
            if ( IsProjectOpen() )
            {
                foreach ( TreeNode tn in treeView.Nodes[0].Nodes )
                {
                    if ( tn.Tag is ProjectFile )
                    {
                        if ( tn.Tag == file )
                            tn.Text = newName;
                    }
                }
            }
        }
        /// <summary>
        /// Renames the currently opened project's node to something new
        /// </summary>
        /// <param name="newName">The new name to assign to the project</param>
        public void RenameProjectNode( string newName )
        {
            if ( IsProjectOpen() )
            {
                treeView.Nodes[0].Text = "Project '" +newName + "'";
            }
        }

        /// <summary>
        /// Shows the object browser
        /// </summary>
        public void ShowObjectBrowser()
        {
            if ( IsObjectBrowserShown() )
            {
                MakeObjectBrowserActive();
                return;
            }

            TabbedDocument tp = new TabbedDocument();
            tp.AllowClose = true;
            tp.Manager = manager;
            tp.Text = "Object Browser";
            SpecialTab st = new SpecialTab();
            st.Type = TabType.ObjectBrowser;
            tp.Tag = st;
            tp.BackColor = SystemColors.Control;

            ObjectBrowser ob = new ObjectBrowser( code, imageList );
            ob.Dock = DockStyle.Fill;
            tp.Controls.Add( ob );

            tp.Open();
        }
        /// <summary>
        /// Makes the object browser the currently active tab
        /// </summary>
        public void MakeObjectBrowserActive()
        {
            foreach ( TabbedDocument tp in manager.Documents )
            {
                if ( IsSpecialTab( tp ) )
                {
                    if ( ( ( SpecialTab )tp.Tag ).Type == TabType.ObjectBrowser )
                        manager.ActiveTabbedDocument = tp;
                }
            }
        }
        /// <summary>
        /// Closes the object browser
        /// </summary>
        public void CloseObjectBrowser()
        {
            if ( IsObjectBrowserShown() )
            {
                foreach ( TabbedDocument tp in manager.Documents)
                {
                    if ( IsSpecialTab( tp ) )
                    {
                        if ( ( ( SpecialTab )tp.Tag ).Type == TabType.ObjectBrowser )
                        {
                            tp.Close();
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Shows the project info tab
        /// </summary>
        public void ShowProjectInfo()
        {
            if ( IsProjectInfoShown() )
            {
                MakeProjectInfoActive();
                return;
            }
            if ( IsProjectOpen() )
            {

                TabbedDocument tp = new TabbedDocument();
                tp.Manager = manager;
                tp.AllowClose = true;
                tp.Text = "Project Properties";
                SpecialTab st = new SpecialTab();
                st.Type = TabType.ProjectInfo;
                tp.Tag = st;
                tp.BackColor = SystemColors.Control;

                ProjectProperties pp = new ProjectProperties( project );
                pp.Dock = DockStyle.Fill;
                tp.Controls.Add( pp );

                tp.Open();
            }
        }
        /// <summary>
        /// Makes the project info tab the currently active tab
        /// </summary>
        public void MakeProjectInfoActive()
        {
            foreach ( TabbedDocument tp in manager.Documents )
            {
                if ( IsSpecialTab( tp ) )
                {
                    if ( ( ( SpecialTab )tp.Tag ).Type == TabType.ProjectInfo )
                        manager.ActiveTabbedDocument = tp;
                }
            }
        }
        /// <summary>
        /// Closes the project info tab
        /// </summary>
        public void CloseProjectInfo()
        {
            if ( IsProjectInfoShown() )
            {
                foreach ( TabbedDocument tp in manager.Documents )
                {
                    if ( IsSpecialTab( tp ) )
                    {
                        if ( ( ( SpecialTab )tp.Tag ).Type == TabType.ProjectInfo )
                        {
                            project = ( ( ProjectProperties )tp.Controls[0] ).Project;
                            project.Saved = false;
                            tp.Close();
                            ReloadNodes();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Displays the settings dialog
        /// </summary>
        public void ShowSettingsDialog()
        {
            SettingsDialog sd = new SettingsDialog( settings );
            sd.ShowDialog();

            settings = sd.Settings;
        }
        
        /// <summary>
        /// Adds a project to the environment, and loads all nodes
        /// </summary>
        /// <param name="proj"></param>
        public void AddProject( Project proj )
        {
            project = proj;

            ClearNodes();

            TreeNode tn = new TreeNode();
            tn.Text = "Project '" + proj.Name + "'";
            tn.ImageKey = "root";
            tn.Tag = proj;
            tn.ContextMenuStrip = projectStrip;
            tn.SelectedImageKey = "root";

            treeView.Nodes.Add( tn );

            foreach ( ProjectFile pf in proj.Files )
            {
                if ( pf.Name.Contains( "\\" ) || pf.Name.Contains( "/" ) )
                {
                    string[] split = pf.Name.Split( '\\', '/' );

                    string fullFolder = "";
                    string lastFolder = "";
                    for ( int x = 0 ; x < split.Length ; x++ )
                    {
                        if ( x == 0 )
                        {
                            lastFolder = "";
                        }
                        if ( x < ( split.Length - 1 ) )
                        {
                            lastFolder = split[x];
                            fullFolder += lastFolder + "\\";
                            string prevFolder = fullFolder.TrimEnd( '\\' ).TrimEnd( lastFolder.ToCharArray() );

                            TreeNode tn_ = new TreeNode();
                            tn_.Tag = fullFolder;
                            tn_.Name = fullFolder;
                            tn_.Text = split[x];
                            tn_.ImageKey = "folder_closed";
                            tn_.SelectedImageKey = "folder_open";
                            TreeNode[] nodes = treeView.Nodes[0].Nodes.Find( fullFolder, true );


                            if ( nodes.Length > 0 )
                                continue;
                            else
                            {
                                if ( String.IsNullOrEmpty( prevFolder ) )
                                    treeView.Nodes[0].Nodes.Add( tn_ );
                                else
                                {
                                    TreeNode[] nodes2 = treeView.Nodes[0].Nodes.Find( prevFolder, true );
                                    if ( nodes2.Length > 0 )
                                        nodes2[0].Nodes.Add( tn_ );
                                    else
                                        treeView.Nodes[0].Nodes.Add( tn_ );
                                }
                            }


                            if ( !Directory.Exists( proj.Path + "\\" + fullFolder ) )
                            {
                                Directory.CreateDirectory( proj.Path + "\\" + fullFolder );
                            }
                        }
                        if ( x == ( split.Length - 1 ) )
                        {
                            if ( !File.Exists( proj.Path + "\\" + pf.Name ) )
                            {
                                pf.Valid = false;
                                pf.Saved = true;

                                string[] names = pf.Name.Split( '\\', '/' );
                                string name = names[names.Length - 1];

                                TreeNode tn_ = new TreeNode();
                                tn_.Text = name;
                                tn_.ContextMenuStrip = codeStrip;
                                tn_.ImageKey = "file_lost";
                                tn_.SelectedImageKey = "file_lost";
                                tn_.Tag = pf;
                                pf.Node = tn_;

                                TreeNode[] nodes = treeView.Nodes[0].Nodes.Find( fullFolder, true );

                                if ( nodes.Length > 0 )
                                    nodes[0].Nodes.Add( tn_ );
                                break;
                            }

                            pf.FullName = proj.Path + "\\" + pf.Name;
                            pf.Saved = true;
                            pf.Valid = true;

                            string[] names_ = pf.Name.Split( '\\', '/' );
                            string name_ = names_[names_.Length - 1];

                            TreeNode tn2 = new TreeNode();
                            tn2.Text = name_;
                            tn2.ContextMenuStrip = codeStrip;
                            tn2.ImageKey = "code";
                            tn2.SelectedImageKey = "code";
                            tn2.Tag = pf;
                            pf.Node = tn2;

                            TreeNode[] nodes_ = treeView.Nodes[0].Nodes.Find( fullFolder, true );

                            if ( nodes_.Length > 0 )
                                nodes_[0].Nodes.Add( tn2 );


                        }
                    }
                }
                else
                {
                    if ( !File.Exists( proj.Path + "\\" + pf.Name ) )
                    {
                        pf.Valid = false;
                        pf.Saved = true;

                        TreeNode tn_ = new TreeNode();
                        tn_.Text = pf.Name;
                        tn_.ContextMenuStrip = codeStrip;
                        tn_.ImageKey = "file_lost";
                        tn_.SelectedImageKey = "file_lost";
                        tn_.Tag = pf;
                        pf.Node = tn_;

                        treeView.Nodes[0].Nodes.Add( tn_ );
                        continue;
                    }

                    pf.FullName = proj.Path + "\\" + pf.Name;
                    pf.Saved = true;
                    pf.Valid = true;

                    TreeNode tn2 = new TreeNode();
                    tn2.Text = pf.Name;
                    tn2.ContextMenuStrip = codeStrip;
                    tn2.ImageKey = "code";
                    tn2.SelectedImageKey = "code";
                    tn2.Tag = pf;
                    pf.Node = tn2;

                    treeView.Nodes[0].Nodes.Add( tn2 );
                }
            }

            treeView.ExpandAll();
        }
        /// <summary>
        /// Removes the currently open project from the envionment, use CloseProject() unless you need to force delete a project
        /// </summary>
        public void RemoveProject()
        {
            project = null;

            Clear();
        }
        /// <summary>
        /// Checks if a project is currently open and creates a new project
        /// </summary>
        public void NewProject()
        {
            if ( IsProjectOpen() )
            {
                if ( !IsProjectSaved() )
                {
                    DialogResult res = Util.ShowQuestion( StringTable.ProjectNotSaved );

                    switch ( res )
                    {
                        case DialogResult.Yes:
                            project.Save();
                            break;
                        case DialogResult.No:
                            break;
                        case DialogResult.Cancel:
                            return;
                    }
                }
                DialogResult result = Util.ShowQuestion( StringTable.OpenProject );

                switch ( result )
                {
                    case DialogResult.Yes:
                        RemoveProject();
                        NewProject();
                        break;
                    case DialogResult.No:
                        return;
                    case DialogResult.Cancel:
                        return;
                }
            }
            else
            {
                project = Project.NewProject();
                if ( project == null )
                    return;

                SaveProject();

                AddProject( project );
            }
        }
        /// <summary>
        /// Shows a dialog and opens an existing project
        /// </summary>
        public void OpenProject()
        {
            if ( IsProjectOpen() )
            {
                if ( !IsProjectSaved() )
                {
                    DialogResult res = Util.ShowQuestion( StringTable.ProjectNotSaved );

                    switch ( res )
                    {
                        case DialogResult.Yes:
                            project.Save();
                            break;
                        case DialogResult.No:
                            break;
                        case DialogResult.Cancel:
                            return;
                    }
                }
                DialogResult result = Util.ShowQuestion( StringTable.OpenProject );

                switch ( result )
                {
                    case DialogResult.Yes:
                        RemoveProject();
                        OpenProject();
                        break;
                    case DialogResult.No:
                        return;
                    case DialogResult.Cancel:
                        return;
                }
            }
            else
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Title = "Open project...";
                ofd.CheckFileExists = true;
                ofd.CheckPathExists = true;
                ofd.DereferenceLinks = true;
                ofd.Filter = "GLua Projects (*.glu)|*.glu";
                ofd.Multiselect = false;

                if ( ofd.ShowDialog() != DialogResult.OK )
                    return;

                project = Project.LoadProject( ofd.FileName );
                if ( project == null )
                    return;
                project.FullName = ofd.FileName;
                project.Path = new FileInfo( ofd.FileName ).Directory.FullName;
                project.Saved = true;
                AddProject( project );
            }
        }
        /// <summary>
        /// Asks to save, closes the project, and calls RemoveProject()
        /// </summary>
        public void CloseProject()
        {
            if ( IsProjectOpen() )
            {
                if ( !IsProjectSaved() )
                {
                    DialogResult res = Util.ShowQuestion( StringTable.ProjectNotSaved );

                    switch ( res )
                    {
                        case DialogResult.Yes:
                            project.Save();
                            RemoveProject();
                            break;
                        case DialogResult.No:
                            RemoveProject();
                            break;
                        case DialogResult.Cancel:
                            return;
                    }
                }
                else
                {
                    RemoveProject();
                }
            }
        }
        /// <summary>
        /// Saves the currently opened project
        /// </summary>
        public void SaveProject()
        {
            if ( IsProjectOpen() )
            {
                if ( project.FullName == "" )
                {
                    SaveFileDialog sfd = new SaveFileDialog();
                    sfd.Title = "Save project...";
                    sfd.AddExtension = true;
                    sfd.DefaultExt = ".glu";
                    sfd.Filter = "GLua Project (*.glu)|*.glu";
                    sfd.OverwritePrompt = true;
                    sfd.SupportMultiDottedExtensions = true;
                    sfd.ValidateNames = true;

                    if ( sfd.ShowDialog() != DialogResult.OK )
                        return;

                    project.FullName = sfd.FileName;
                    project.Path = new FileInfo( project.FullName ).Directory.FullName;
                    foreach ( ProjectFile pf in project.Files )
                    {
                        pf.FullName = project.Path + "\\" + pf.Name;
                    }
                    project.Save();
                }
                else
                {
                    project.Save();
                }
            }
        }
        /// <summary>
        /// Displays a dialog and renames the project
        /// </summary>
        public void RenameProject()
        {
            if ( IsProjectOpen() )
            {
                RenameResult rr = RenameDialog.ShowDialog( project.Name );

                foreach ( char chr in rr.RenameRes.ToCharArray() )
                {
                    foreach ( char invalidChar in Path.GetInvalidFileNameChars() )
                    {
                        if ( chr == invalidChar )
                        {
                            Util.ShowError( StringTable.InvalidProjectName );
                            RenameProject();
                            return;
                        }
                    }
                }

                switch ( rr.DialogRes )
                {
                    case DialogResult.OK:
                        bool pInfoShown = false;
                        if ( IsProjectInfoShown() )
                        {
                            pInfoShown = true;
                            CloseProjectInfo();
                        }
                        RenameProjectNode( rr.RenameRes );
                        project.Name = rr.RenameRes;
                        if ( pInfoShown )
                            ShowProjectInfo();
                        project.Saved = false;
                        break;
                    case DialogResult.Cancel:
                        return;
                }
            }
        }

        /// <summary>
        /// Shows a dialog and opens a file which will not be part of the project
        /// </summary>
        public void OpenFile()
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "Open file...";
            ofd.CheckFileExists = true;
            ofd.CheckPathExists = true;
            ofd.DereferenceLinks = true;
            ofd.Filter = "Lua Files (*.lua)|*.lua|All Files (*.*)|*.*";
            ofd.Multiselect = true;

            if ( ofd.ShowDialog() != DialogResult.OK )
                return;

            foreach ( string file in ofd.FileNames )
            {
                OpenedFile of = new OpenedFile();
                of.FullName = file;
                FileInfo fi = new FileInfo(file);
                of.Name = fi.Name;
                of.Saved = true;

                ShowOpenedFile( of );
            }
        }
        /// <summary>
        /// Opens a file directly from a filename
        /// </summary>
        /// <param name="fname">The filename to open</param>
        public void OpenFile( string fname )
        {
            OpenedFile of = new OpenedFile();
            of.FullName = fname;
            FileInfo fi = new FileInfo( fname );
            of.Name = fi.Name;
            of.Saved = true;

            ShowOpenedFile( of );
        }
        /// <summary>
        /// Creates and shows a tab for an opened file which is sperate from the project
        /// </summary>
        /// <param name="file">The file to open</param>
        public void ShowOpenedFile( OpenedFile file )
        {
            TabbedDocument tp = new TabbedDocument();
            tp.Manager = manager;
            tp.AllowClose = true;
            tp.Text = file.Name;

            if ( file.Editor == null )
                file.Editor = CreateTextEditor();

            file.Editor.Tag = true;
            file.Editor.LoadFile( file.FullName );
            file.Editor.Tag = false;
            tp.Controls.Add( file.Editor );
            tp.Tag = file;
            tp.BackColor = SystemColors.Control;

            tp.Open();
        }
        /// <summary>
        /// Adds a previously created project file to the project
        /// </summary>
        /// <param name="file">The file to add</param>
        public void AddProjectFile( ProjectFile file )
        {
            project.Files.Add( file );

            TreeNode tn = new TreeNode();
            tn.Text = file.Name;
            tn.ImageKey = "code";
            tn.ContextMenuStrip = codeStrip;
            tn.SelectedImageKey = "code";
            tn.Tag = file;
            file.Node = tn;
            treeView.Nodes[0].Nodes.Add( tn );
            treeView.ExpandAll();
        }
        /// <summary>
        /// Removes a previously created project file from the project
        /// </summary>
        /// <param name="file">The file to remove</param>
        public void RemoveProjectFile( ProjectFile file )
        {
            if ( IsProjectOpen() )
            {
                DialogResult res = FileRemoveDialog.ShowDialog( file.Name );

                switch ( res )
                {
                    case DialogResult.Yes:
                        File.Delete( file.FullName );
                        CloseCodeTab( file );
                        RemoveNode( file );
                        project.Files.Remove( file );
                        project.Saved = false;
                        break;
                    case DialogResult.No:
                        CloseCodeTab( file );
                        RemoveNode( file );
                        project.Files.Remove( file );
                        project.Saved = false;
                        break;
                    case DialogResult.Cancel:
                        return;
                }

            }
        }
        /// <summary>
        /// Displays a dialog and renames a project file
        /// </summary>
        /// <param name="file">The file to rename</param>
        public void RenameProjectFile( ProjectFile file )
        {
            if ( IsProjectOpen() )
            {
                RenameResult rr = RenameDialog.ShowDialog( file.Name );

                foreach ( char chr in rr.RenameRes.ToCharArray() )
                {
                    foreach ( char invalidChar in Path.GetInvalidFileNameChars() )
                    {
                        if ( chr == invalidChar )
                        {
                            // invalid character, call RenameProjectFile() again
                            Util.ShowError( StringTable.InvalidFileName );
                            RenameProjectFile( file );
                            return;
                        }
                    }
                }

                switch ( rr.DialogRes )
                {
                    case DialogResult.OK:
                        if ( file.Name == rr.RenameRes )
                            return;

                        File.Copy( file.FullName, file.FullName.Replace( file.Name, rr.RenameRes ) );
                        File.Delete( file.FullName );
                        RenameNode( file, rr.RenameRes );
                        RenameCodeTab( file, rr.RenameRes );
                        file.FullName = file.FullName.Replace( file.Name, rr.RenameRes );
                        file.Name = rr.RenameRes;
                        file.Saved = false;
                        project.Saved = false;
                        break;
                    case DialogResult.Cancel:
                        return;
                }
            }
        }
        /// <summary>
        /// Makes a previously opened project file active
        /// </summary>
        /// <param name="file">The file to activate</param>
        public void MakeProjectFileActive( ProjectFile file )
        {
            foreach ( TabbedDocument tp in manager.Documents )
            {
                if ( !IsSpecialTab( tp ) )
                {
                    if ( tp.Tag == file )
                        manager.ActiveTabbedDocument = tp;
                }
            }
        }
        /// <summary>
        /// Creates a tab and shows a project file, if the tab is already created it is made active
        /// </summary>
        /// <param name="file">The file to show</param>
        public void ShowProjectFile( ProjectFile file )
        {
            if ( IsProjectFileShown( file ) )
            {
                MakeProjectFileActive( file );
                return;
            }

            if ( File.Exists( file.FullName ) )
            {
                file.Valid = true;
                file.Node.ImageKey = "code";
                file.Node.SelectedImageKey = "code";
            }
            else
            {
                Util.ShowError( StringTable.ProjectFileLost.Replace( "%FILE%", file.Name ) );
                return;
            }

            if ( !fileLoading )
            {
                fileLoading = true;

                TabbedDocument tp = new TabbedDocument();
                tp.Manager = manager;
                tp.AllowClose = true;
                tp.Text = file.Name;

                if ( file.Editor == null || file.Editor.IsDisposed )
                    file.Editor = CreateTextEditor();

                file.Editor.Tag = true;
                file.Editor.LoadFile( file.FullName );
                file.Editor.Tag = false;
                tp.Controls.Add( file.Editor );
                tp.Tag = file;
                tp.BackColor = SystemColors.Control;

                tp.Open();

                fileLoading = false;
            }
        }

        /// <summary>
        /// Saves a project file
        /// </summary>
        /// <param name="file">The project file to save</param>
        public void SaveProjectFile( ProjectFile file )
        {
            file.Save();
        }

        /// <summary>
        /// Shows a special tab
        /// </summary>
        /// <param name="specialTab">The special tab to open</param>
        public void ShowSpecialTab( SpecialTab specialTab )
        {
            if ( IsSpecialTabShown( specialTab ) )
                return;

            switch ( specialTab.Type )
            {
                case TabType.ObjectBrowser:
                    ShowObjectBrowser();
                    break;
                case TabType.ProjectInfo:
                    ShowProjectInfo();
                    break;
            }
        }
        /// <summary>
        /// Checks for special tabs and files, asks to save, and then closes the currently active tab
        /// </summary>
        public void CloseActiveTab()
        {
            if ( IsSpecialTab( (TabbedDocument)manager.ActiveTabbedDocument ) )
            {
                if ( ( ( SpecialTab )manager.ActiveTabbedDocument.Tag ).Type == TabType.ObjectBrowser )
                {
                    CloseObjectBrowser();
                    return;
                }
                if ( ( ( SpecialTab )manager.ActiveTabbedDocument.Tag ).Type == TabType.ProjectInfo )
                {
                    CloseProjectInfo();
                    return;
                }
            }
            else
            {
                if ( manager.ActiveTabbedDocument.Tag is ProjectFile )
                {
                    ProjectFile pf = ( ProjectFile )manager.ActiveTabbedDocument.Tag;

                    if ( !pf.Saved )
                    {
                        DialogResult res = Util.ShowQuestion( StringTable.FileNotSaved.Replace( "%FILE%", pf.Name ) );

                        switch ( res )
                        {
                            case DialogResult.Yes:
                                pf.Save();
                                manager.ActiveTabbedDocument.Close();
                                break;
                            case DialogResult.No:
                                manager.ActiveTabbedDocument.Close();
                                break;
                            case DialogResult.Cancel:
                                return;

                        }
                    }
                    else
                    {
                        manager.ActiveTabbedDocument.Close();
                    }
                }
                else if ( manager.ActiveTabbedDocument.Tag is OpenedFile )
                {
                    OpenedFile of = ( OpenedFile )manager.ActiveTabbedDocument.Tag;

                    if ( !of.Saved )
                    {
                        DialogResult res = Util.ShowQuestion( StringTable.FileNotSaved.Replace( "%FILE%", of.Name ) );

                        switch ( res )
                        {
                            case DialogResult.Yes:
                                of.Save();
                                manager.ActiveTabbedDocument.Close();
                                break;
                            case DialogResult.No:
                                manager.ActiveTabbedDocument.Close();
                                break;
                            case DialogResult.Cancel:
                                return;

                        }
                    }
                    else
                    {
                        manager.ActiveTabbedDocument.Close();
                    }
                }
            }
        }
        /// <summary>
        /// Closes a code tab without saving
        /// </summary>
        /// <param name="file"></param>
        public void CloseCodeTab( ProjectFile file )
        {
            foreach ( TabbedDocument td in manager.Documents )
            {
                if ( !IsSpecialTab( td ) )
                {
                    if ( td.Tag == file )
                        td.Close();
                }
            }
        }
        /// <summary>
        /// Renames a a code tab of a project file
        /// </summary>
        /// <param name="file">The project file to rename</param>
        /// <param name="newName">The new name to give the code tab</param>
        public void RenameCodeTab( ProjectFile file, string newName)
        {
            foreach ( TabbedDocument td in manager.Documents )
            {
                if ( !IsSpecialTab( td ) )
                {
                    if ( td.Tag == file )
                        td.Text = newName;
                }
            }
        }
        /// <summary>
        /// Asks to save and closes all tabs except the active tab
        /// </summary>
        public void CloseAllButActiveTab()
        {
            foreach ( TabbedDocument tp in manager.Documents )
            {
                if ( tp == manager.ActiveTabbedDocument )
                    continue;

                if ( IsSpecialTab( tp ) )
                {
                    if ( ( ( SpecialTab )tp.Tag ).Type == TabType.ObjectBrowser )
                        CloseObjectBrowser();
                    if ( ( ( SpecialTab )tp.Tag ).Type == TabType.ProjectInfo )
                        CloseProjectInfo();
                }
                else
                {
                    if ( tp.Tag is ProjectFile )
                    {
                        ProjectFile pf = ( ProjectFile )tp.Tag;

                        if ( !pf.Saved )
                        {
                            DialogResult res = Util.ShowQuestion( StringTable.FileNotSaved.Replace( "%FILE%", pf.Name ) );

                            switch ( res )
                            {
                                case DialogResult.Yes:
                                    pf.Save();
                                    tp.Close();
                                    break;
                                case DialogResult.No:
                                    tp.Close();
                                    break;
                                case DialogResult.Cancel:
                                    return;

                            }
                        }
                        else
                        {
                            tp.Close();
                        }
                    }
                    if ( tp.Tag is OpenedFile )
                    {
                        OpenedFile of = ( OpenedFile )tp.Tag;

                        if ( !of.Saved )
                        {
                            DialogResult res = Util.ShowQuestion( StringTable.FileNotSaved.Replace( "%FILE%", of.Name ) );

                            switch ( res )
                            {
                                case DialogResult.Yes:
                                    of.Save();
                                    tp.Close();
                                    break;
                                case DialogResult.No:
                                    tp.Close();
                                    break;
                                case DialogResult.Cancel:
                                    return;

                            }
                        }
                        else
                        {
                            tp.Close();
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Attempts to save the currently active tab
        /// </summary>
        public void SaveActiveTab()
        {
            if ( !IsActiveTabSpecial() )
            {
                if ( manager.ActiveTabbedDocument.Tag is ProjectFile )
                {
                    ProjectFile pf = ( ProjectFile )manager.ActiveTabbedDocument.Tag;
                    SaveProjectFile( pf );
                }
                else if ( manager.ActiveTabbedDocument.Tag is OpenedFile )
                {
                    OpenedFile of = ( OpenedFile )manager.ActiveTabbedDocument.Tag;
                    of.Save();
                }
            }
            else
            {
                if ( ((SpecialTab)manager.ActiveTabbedDocument.Tag).Type == TabType.ProjectInfo )
                {
                    ( ( ProjectProperties )manager.ActiveTabbedDocument.Controls[0] ).Save();
                }
            }
        }

        /// <summary>
        /// Shows a save dialog and generates an info file
        /// </summary>
        public void GenerateInfoFile()
        {
            if ( IsProjectOpen() )
            {
                SaveFileDialog sfd = new SaveFileDialog();
                sfd.AddExtension = true;
                sfd.DefaultExt = ".txt";
                sfd.FileName = "info.txt";
                sfd.Filter = "Info Files (info.txt)|info.txt";
                sfd.InitialDirectory = project.Path;
                sfd.Title = "Save Info File...";

                if ( sfd.ShowDialog() != DialogResult.OK )
                    return;

                InfoGenerator.GenerateInfo( sfd.FileName, project );
            }
        }

        /// <summary>
        /// Checks for any active code files and projects, asks to save, closes and removes tabs, and saves settings
        /// </summary>
        /// <returns>True if the exit completed successfully, otherwise false if the user decided to cancel</returns>
        public bool Exit()
        {
            if ( IsProjectOpen() )
            {
                if ( !IsProjectSaved() )
                {
                    DialogResult res = Util.ShowQuestion( StringTable.ProjectNotSaved );

                    switch ( res )
                    {
                        case DialogResult.Yes:
                            SaveProject();
                            RemoveProject();
                            break;
                        case DialogResult.No:
                            RemoveProject();
                            break;
                        case DialogResult.Cancel:
                            return false;
                    }
                }
                RemoveProject();
            }

            if ( IsCodeTabOpen() )
            {
                foreach ( TabbedDocument td in manager.Documents )
                {
                    if ( !IsSpecialTab( td ) )
                    {
                        if ( td.Tag is OpenedFile )
                        {
                            OpenedFile of = ( OpenedFile )td.Tag;

                            if ( !of.Saved )
                            {

                                DialogResult res = Util.ShowQuestion( StringTable.FileNotSaved.Replace( "%FILE%", of.Name ) );

                                switch ( res )
                                {
                                    case DialogResult.Yes:
                                        of.Save();
                                        td.Close();
                                        break;
                                    case DialogResult.No:
                                        td.Close();
                                        break;
                                    case DialogResult.Cancel:
                                        return false;
                                }
                            }
                        }
                    }
                }
            }

            Settings.Save( Application.StartupPath + "\\data\\settings.xml", settings );

            return true;
        }

        /// <summary>
        /// Shows the add new item dialog and adds a new item
        /// </summary>
        public void AddNewItem()
        {
            if ( IsProjectOpen() )
            {
                NewFileDialog nfd = new NewFileDialog();

                if ( nfd.ShowDialog() != DialogResult.OK )
                    return;

                if ( File.Exists( project.Path + "\\" + nfd.FileName ) )
                {
                    Util.ShowError( StringTable.FileAlreadyExists );
                    AddNewItem();
                    return;
                }

                File.Create( project.Path + "\\" + nfd.FileName ).Close();
                File.WriteAllText( project.Path + "\\" + nfd.FileName, nfd.SelectedTemplate.Code );

                ProjectFile pf = new ProjectFile();
                pf.Name = nfd.FileName;
                pf.Saved = false;
                pf.FullName = project.Path + "\\" + pf.Name;

                project.Saved = false;

                AddProjectFile( pf );
            }
        }
        /// <summary>
        /// Shows the open file dialog and adds an existing item
        /// </summary>
        public void AddExistingItem()
        {
            if ( IsProjectOpen() )
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter = "Lua Files (*.lua)|*.lua";
                ofd.Multiselect = true;
                ofd.Title = "Add Existing Item...";

                if ( ofd.ShowDialog() != DialogResult.OK )
                    return;

                foreach ( string name in ofd.FileNames )
                {
                    FileInfo fi = new FileInfo( name );

                    if ( DoesProjectFileExist( fi.Name ) )
                    {
                        Util.ShowError( StringTable.FileAlreadyAdded );
                        continue;
                    }
                    ProjectFile pf = new ProjectFile();
                    pf.Name = fi.Name;
                    pf.Saved = true;
                    pf.FullName = project.Path + "\\" + pf.Name;

                    AddProjectFile( pf );
                }

                project.Saved = false;
            }
        }

        /// <summary>
        /// Creates an lua specific syntax editor control
        /// </summary>
        /// <returns>A customized syntax edit control used for editing lua files</returns>
        public SyntaxEdit CreateTextEditor()
        {
            SyntaxEdit se = new SyntaxEdit();

            se.Lexer = new Lexer();
            se.Lexer.LoadScheme( Application.StartupPath + "\\data\\lua_scheme.xml" );

            se.Dock = DockStyle.Fill;

            se.Gutter.Options = GutterOptions.PaintLineModificators | GutterOptions.PaintLineNumbers;
            se.Source.HighlightUrls = true;
            se.Source.UndoOptions = UndoOptions.AllowUndo | UndoOptions.UndoAfterSave;
            se.TextChanged += new EventHandler( se_TextChanged );
            se.NeedCodeCompletion += new CodeCompletionEvent( se_NeedCodeCompletion );
            se.MouseClick += new MouseEventHandler( se_MouseClick );
            se.Braces.BracesOptions = BracesOptions.Highlight | BracesOptions.HighlightBounds;
            se.Braces.ClosingBraces = new char[] { ')', ']', '}' };
            se.Braces.OpenBraces = new char[] { '(', '[', '{' };
            se.CodeCompletionChars = new char[] { '.', ':', '(', ',' };
            se.DisplayLines.AllowOutlining = true;
            se.DisplayLines.OutlineOptions = OutlineOptions.DrawButtons | OutlineOptions.DrawLines | OutlineOptions.ShowHints;
            se.Gutter.LineNumbersAlignment = StringAlignment.Far;
            se.Gutter.LineNumbersStart = 1;
            se.Gutter.LineNumbersLeftIndent = 20;
            se.IndentOptions = IndentOptions.AutoIndent;
            se.Scrolling.Options = ScrollingOptions.SmoothScroll | ScrollingOptions.ShowScrollHint;

            // file menu shortcuts
            se.KeyList.Add( Keys.Control | Keys.Shift | Keys.S, new KeyEvent( se_SaveProject ) );
            se.KeyList.Add( Keys.Control | Keys.S, new KeyEvent( se_SaveActive ) );
            se.KeyList.Add( Keys.Control | Keys.Shift | Keys.N, new KeyEvent( se_NewProject ) );
            se.KeyList.Add( Keys.Control | Keys.Shift | Keys.O, new KeyEvent( se_OpenProject ) );
            se.KeyList.Add( Keys.Control | Keys.O, new KeyEvent( se_OpenFile ) );
            se.KeyList.Add( Keys.Alt | Keys.F4, new KeyEvent( se_Exit ) );

            // view menu shortcuts
            se.KeyList.Add( Keys.Control | Keys.Alt | Keys.E, new KeyEvent( se_ShowProjectExplorer ) );
            se.KeyList.Add( Keys.Control | Keys.Alt | Keys.B, new KeyEvent( se_ShowObjectBrowser ) );
            se.KeyList.Add( Keys.Control | Keys.Alt | Keys.T, new KeyEvent( se_ShowTaskList ) );
            se.KeyList.Add( Keys.Control | Keys.Alt | Keys.O, new KeyEvent( se_ShowOutput ) );

            // project menu shortcuts
            se.KeyList.Add( Keys.Control | Keys.Shift | Keys.A, new KeyEvent( se_AddNewItem ) );
            se.KeyList.Add( Keys.Alt | Keys.Shift | Keys.A, new KeyEvent( se_AddExistingItem ) );

            // help menu shortcuts
            se.KeyList.Add( Keys.F1, new KeyEvent( se_GmodWiki ) );
            se.KeyList.Add( Keys.Alt | Keys.F1, new KeyEvent( se_FPLuaScripting ) );
            se.KeyList.Add( Keys.Control | Keys.F1, new KeyEvent( se_GLuaHome ) );

            // code edit shortcuts
            se.KeyList.Add( Keys.Control | Keys.J, new KeyEvent( se_WordCompletion ) );


            se.DefaultMenu.MenuItems.Clear();
            se.Refresh();
            se.Update();

            return se;
        }
        /// <summary>
        /// Checks if the environment can undo
        /// </summary>
        /// <returns>True if undo is allowed, otherwise false</returns>
        public bool CanUndo()
        {
            if ( IsTabOpen() )
            {
                if ( IsActiveTabSpecial() )
                    return false;
                else
                {
                    SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                    return edit.Source.CanUndo();
                }
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// Checks if the environment can redo
        /// </summary>
        /// <returns>True if redo is allowed, otherwise false</returns>
        public bool CanRedo()
        {
            if ( IsTabOpen() )
            {
                if ( IsActiveTabSpecial() )
                    return false;
                else
                {
                    SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                    return edit.Source.CanRedo();
                }
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// Checks if the environment can cut
        /// </summary>
        /// <returns>True if cut is allowed, otherwise false</returns>
        public bool CanCut()
        {
            if ( IsTabOpen() )
            {
                if ( IsActiveTabSpecial() )
                    return false;
                else
                {
                    SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                    return edit.Selection.CanCut();
                }
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// Checks if the environment can copy
        /// </summary>
        /// <returns>True if copy is allowed, otherwise false</returns>
        public bool CanCopy()
        {
            if ( IsTabOpen() )
            {
                if ( IsActiveTabSpecial() )
                    return false;
                else
                {
                    SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                    return edit.Selection.CanCopy();
                }
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// Checks if the environment can paste
        /// </summary>
        /// <returns>True if paste is allowed, otherwise false</returns>
        public bool CanPaste()
        {
            if ( IsTabOpen() )
            {
                if ( IsActiveTabSpecial() )
                    return false;
                else
                {
                    SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                    return edit.Selection.CanPaste();
                }
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// Checks if the environment can delete
        /// </summary>
        /// <returns>True if delete is allowed, otherwise false</returns>
        public bool CanDelete()
        {
            return IsCodeTabOpen();
        }
        /// <summary>
        /// Undo's the last change in the editor
        /// </summary>
        public void Undo()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                edit.Source.Undo();
            }
        }
        /// <summary>
        /// Redo's the last change in the editor
        /// </summary>
        public void Redo()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                edit.Source.Redo();
            }
        }
        /// <summary>
        /// Cuts text from the editor
        /// </summary>
        public void Cut()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                edit.Selection.Cut();
            }
        }
        /// <summary>
        /// Copies text from the editor
        /// </summary>
        public void Copy()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                edit.Selection.Copy();
            }
        }
        /// <summary>
        /// Pastes text to the editor
        /// </summary>
        public void Paste()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                edit.Selection.Paste();
            }
        }
        /// <summary>
        /// Deletes the currently selected text in the editor
        /// </summary>
        public void Delete()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                edit.Selection.Delete();

                if ( edit.Selection.IsEmpty )
                    edit.Selection.DeleteRight();
            }
        }
        /// <summary>
        /// Selects all text in the editor
        /// </summary>
        public void SelectAll()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                edit.Selection.SelectAll();
            }
        }
        /// <summary>
        /// Displays a find dialog
        /// </summary>
        public void FindDialog()
        {
            if ( IsTabOpen() )
            {
                if ( IsActiveTabSpecial() )
                    return;
                else
                {
                    SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                    edit.SearchDialog.Execute( edit, true, false );
                }
            }
        }
        /// <summary>
        /// Displays a find dialog with pre-entered text in the textbox
        /// </summary>
        /// <param name="input">The text to have displayed</param>
        // Apparently the editor control doesn't support this
        // A quit edit in the SE library can change this
        public void FindDialog( string input )
        {
            if ( IsTabOpen() )
            {
                if ( IsActiveTabSpecial() )
                    return;
                else
                {
                    SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                    edit.SearchDialog.Execute( edit, true, false );
                }
            }
        }
        /// <summary>
        /// Displays the replace dialog
        /// </summary>
        public void ReplaceDialog()
        {
            if ( IsTabOpen() )
            {
                if ( IsActiveTabSpecial() )
                    return;
                else
                {
                    SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                    edit.SearchDialog.Execute( edit, true, true );
                }
            }
        }
        /// <summary>
        /// Unindents the currently selected line(s)
        /// </summary>
        public void Unindent()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                edit.Selection.UnIndent();
            }
        }
        /// <summary>
        /// Indents the currently selected line(s)
        /// </summary>
        public void Indent()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                edit.Selection.Indent();
            }
        }
        /// <summary>
        /// Comments the currently selected line(s)
        /// </summary>
        public void CommentLine()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                if ( edit.Selection.SelectedText.Contains( "\n" ) )
                    edit.Selection.SelectedText = "/* " + edit.Selection.SelectedText + " */";
                else
                    edit.Selection.SelectedText = "// " + edit.Selection.SelectedText;
            }
        }
        /// <summary>
        /// Uncomments the currently selected line(s)
        /// </summary>
        public void UncommentLine()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                if ( edit.Selection.SelectedText.Contains( "\n" ) )
                    edit.Selection.SelectedText = edit.Selection.SelectedText.Replace( "/*", "" ).Replace( "*/", "" );
                else
                    edit.Selection.SelectedText = edit.Selection.SelectedText.Replace( "//", "" );
            }
        }
        /// <summary>
        /// Forces the code completion window to display for the active tab
        /// </summary>
        public void DisplayCodeCompletion()
        {
            if ( IsActiveTabSpecial() )
                return;
            else
            {
                SyntaxEdit edit = ( SyntaxEdit )manager.ActiveTabbedDocument.Controls[0];
                edit.CompleteWord();
            }
        }

        private void se_TextChanged( object sender, EventArgs e )
        {
            SyntaxEdit edit = ( SyntaxEdit )sender;

            if ( ( bool )edit.Tag == true ) // .Tag == true when a file is loading
                return;
            if ( edit.Parent.Tag is ProjectFile )
            {
                ProjectFile pf = ( ProjectFile )edit.Parent.Tag;

                if ( pf.Saved )
                {
                    edit.Parent.Text += "*";
                }
                pf.Saved = false;
                edit.Parent.Tag = pf;
            }
            else if ( edit.Parent.Tag is OpenedFile )
            {
                OpenedFile of = ( OpenedFile )edit.Parent.Tag;

                if ( of.Saved )
                {
                    edit.Parent.Text += "*";
                }
                of.Saved = false;
                edit.Parent.Tag = of;
            }


            if ( edit.Text.Length <= 0 )
                return;

            IStrItem item = edit.Lines.GetItem( edit.Position.Y );

            if ( edit.Position.X <= 0 )
                return;

            char keyPressed = item.String[edit.Position.X - 1];
            int A = ( int )'A';
            int Z = ( int )'z';
            int key = ( int )keyPressed;
            if ( key >= A && key <= Z )
                if ( settings.AutoComplete == AutoCompleteType.AlwaysOn )
                    edit.CompleteWord();
            if ( keyPressed == '.' || keyPressed == ':' )
                edit.ListMembers();
            if ( keyPressed == '(' )
                edit.ParameterInfo();


        }
        private void se_NeedCodeCompletion( object sender, CodeCompletionArgs e )
        {
            SyntaxEdit edit = ( SyntaxEdit )sender;
            switch ( e.CompletionType )
            {
                case CodeCompletionType.ListMembers:
                    #region ListMembers
                    char tChar = '\0';
                    string tStr = "";
                    for ( int x = edit.Position.X ; x > 1 ; x-- )
                    {
                        IStrItem stritem = edit.Lines.GetItem( edit.Position.Y );
                        tChar = stritem.String[x - 2];
                        if ( IsSplitChar( tChar ) )
                            break;
                        tStr = tChar + tStr;
                    }

                    if ( !code.IsLibrary( tStr ) )
                        return;

                    Member[] members = code.GetMembers( tStr );

                    IListMembers memlist = new ListMembers();
                    memlist.Images = imageList;
                    memlist.ShowDescriptions = true;
                    memlist.ShowHints = true;
                    memlist.UseHtmlFormatting = true;

                    foreach ( Member memb in members )
                    {
                        if ( memb is Function )
                        {
                            IListMember m = memlist.AddMember();
                            Function func = ( Function )memb;
                            m.Name = func.Name;
                            switch ( func.Type )
                            {
                                case "SERVER":
                                    m.ImageIndex = 3;
                                    break;
                                case "CLIENT":
                                    m.ImageIndex = 4;
                                    break;
                                case "SHARED":
                                    m.ImageIndex = 5;
                                    break;
                            }
                            m.Description = "<FONT COLOR=\"#0000FF\">" + func.Return + "</FONT> <B>" + memb.Library.Name + ":" + func.Name + "</B>(<FONT Color=\"#00FF00\">" + func.Params + "</FONT>)<br>" + func.Description;
                        }
                        else if ( memb is Property )
                        {
                            IListMember m = memlist.AddMember();
                            Property prop = ( Property )memb;
                            m.Name = prop.Name;
                            switch ( prop.Type )
                            {
                                case "SERVER":
                                    m.ImageIndex = 3;
                                    break;
                                case "CLIENT":
                                    m.ImageIndex = 4;
                                    break;
                                case "SHARED":
                                    m.ImageIndex = 5;
                                    break;
                            }
                            m.Description = prop.DataType + " " + memb.Library.Name + "." + prop.Name + "<br>" + prop.Description;
                        }
                    }

                    e.NeedShow = true;
                    e.Provider = memlist;
                    e.ToolTip = false;

                    break;
                    #endregion
                case CodeCompletionType.CompleteWord:
                    #region CompleteWord
                    tChar = '\0';
                    tStr = "";
                    int pos = 0;
                    for ( int x = edit.Position.X ; x > 1 ; x-- )
                    {
                        IStrItem stritem = edit.Lines.GetItem( edit.Position.Y );
                        tChar = stritem.String[x - 2];
                        if ( IsSplitChar( tChar ) )
                        {
                            pos = x;
                            break;
                        }
                        tStr = tChar + tStr;
                    }
                    if ( settings.AutoComplete == AutoCompleteType.AlwaysOn )
                        e.Init( CodeCompletionType.CompleteWord, new Point( edit.Position.X - 1, edit.Position.Y ), true );
                    else
                        e.Init( CodeCompletionType.CompleteWord, new Point( pos - 1, edit.Position.Y ), true );


                    if ( settings.AutoComplete == AutoCompleteType.AlwaysOn )
                        if ( !String.IsNullOrEmpty( tStr ) )
                            return;

                    Library[] libs = code.Libraries.ToArray();

                    IListMembers list = new ListMembers();
                    list.Images = imageList;
                    list.ShowDescriptions = true;
                    list.ShowHints = true;
                    list.UseHtmlFormatting = true;

                    foreach ( string keyword in code.Keywords )
                    {
                        IListMember m = list.AddMember();
                        m.Name = keyword;
                        m.ImageIndex = 7;
                        m.Description = keyword;
                    }
                    foreach ( Library lib in libs )
                    {
                        if ( lib.Name == "global" )
                            continue;

                        IListMember m = list.AddMember();
                        m.Name = lib.Name;
                        m.ImageIndex = 6;
                        m.Description = lib.Name + "<br>" + lib.Description;
                    }
                    Member[] membs = code.GetMembers( "global" );
                    foreach ( Member memb in membs )
                    {
                        if ( memb is Function )
                        {
                            IListMember m = list.AddMember();
                            Function func = ( Function )memb;
                            m.Name = func.Name;
                            switch ( func.Type )
                            {
                                case "SERVER":
                                    m.ImageIndex = 3;
                                    break;
                                case "CLIENT":
                                    m.ImageIndex = 4;
                                    break;
                                case "SHARED":
                                    m.ImageIndex = 5;
                                    break;
                            }
                            m.Description = "<FONT COLOR=\"#0000FF\">" + func.Return + "</FONT> <B>" + func.Name + "</B>(<FONT Color=\"#00FF00\">" + func.Params + "</FONT>)<br>" + func.Description;
                        }
                        else if ( memb is Property )
                        {
                            IListMember m = list.AddMember();
                            Property prop = ( Property )memb;
                            m.Name = prop.Name;
                            switch ( prop.Type )
                            {
                                case "SERVER":
                                    m.ImageIndex = 3;
                                    break;
                                case "CLIENT":
                                    m.ImageIndex = 4;
                                    break;
                                case "SHARED":
                                    m.ImageIndex = 5;
                                    break;
                            }
                            m.Description = prop.DataType + " " + prop.Name + "<br>" + prop.Description;
                        }
                    }
                    if ( settings.AutoComplete == AutoCompleteType.Restrictive )
                    {
                        int index = -1;
                        for ( int x = 0 ; x < list.Count ; x++ )
                        {
                            if ( list[x].Name.StartsWith( tStr, StringComparison.InvariantCulture ) )
                            {
                                index = x;
                                break;
                            }
                        }

                        if ( index != -1 )
                            list.SelIndex = index;
                    }

                    e.NeedShow = true;
                    e.Provider = list;
                    e.ToolTip = false;
                    break;
                    #endregion
                case CodeCompletionType.ParameterInfo:
                    #region ParameterInfo
                    break;
                    #endregion
            }
        }
        private void se_MouseClick( object sender, MouseEventArgs e )
        {
            if ( e.Button == MouseButtons.Right )
            {
                editStrip.Show( ( Control )sender, e.Location );
            }
        }
        private void se_SaveProject()
        {
            SaveProject();
        }
        private void se_SaveActive()
        {
            SaveActiveTab();
        }
        private void se_NewProject()
        {
            NewProject();
        }
        private void se_OpenProject()
        {
            OpenProject();
        }
        private void se_OpenFile()
        {
            OpenFile();
        }
        private void se_Exit()
        {
            if ( Exit() )
                Application.Exit();
        }
        private void se_ShowProjectExplorer()
        {
            explorer.Open();
        }
        private void se_ShowObjectBrowser()
        {
            ShowObjectBrowser();
        }
        private void se_ShowTaskList()
        {
            taskList.Open();
        }
        private void se_ShowOutput()
        {
            output.Open();
        }
        private void se_AddNewItem()
        {
            AddNewItem();
        }
        private void se_AddExistingItem()
        {
            AddExistingItem();
        }
        private void se_GmodWiki()
        {
            try
            {
                Process.Start( "http://garrysmod.com/wiki/" );
            }
            catch { }
        }
        private void se_FPLuaScripting()
        {
            try
            {
                Process.Start( "http://forums.facepunchstudios.com/forumdisplay.php?f=65" );
            }
            catch { }
        }
        private void se_GLuaHome()
        {
            try
            {
                Process.Start( "http://glua.net/" );
            }
            catch { }
        }
        private void se_WordCompletion()
        {
            DisplayCodeCompletion();
        }
        private void treeView_NodeMouseClick( object sender, TreeNodeMouseClickEventArgs e )
        {
            if ( e.Button == MouseButtons.Right )
                treeView.SelectedNode = e.Node;
        }
    }
}
